package com.dave.gd.core.base.entity.impl;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.persistence.Table;

import org.apache.commons.beanutils.PropertyUtils;

import com.dave.gd.common.log.ILogger;
import com.dave.gd.common.log.LoggerFactory;
import com.dave.gd.common.util.ObjectUtils;
import com.dave.gd.common.util.StringUtils;
import com.dave.gd.common.util.spring.ApplicationContextUtil;
import com.dave.gd.core.base.entity.IAttrSpec;
import com.dave.gd.core.base.entity.IAttrValue;
import com.dave.gd.core.base.entity.IBaseEntity;
import com.dave.gd.core.base.entity.IBaseEntityMeta;
import com.dave.gd.core.base.entity.IMetaEntity;
import com.dave.gd.core.base.entity.ISysClass;
import com.dave.gd.core.base.entity.OpType;
import com.dave.gd.core.base.repository.IBaseRepository;
import com.dave.gd.core.base.repository.RepositoryRegister;
import com.dave.gd.core.common.sequence.util.SequenceFetchUtils;
import com.dave.gd.core.contants.BaseUnitConstants;
import com.dave.gd.core.context.SessionContext;
import com.dave.gd.core.rule.ERuleMode;
import com.dave.gd.core.rule.ERuleRes;
import com.dave.gd.core.rule.IRuleableEntity;
import com.dave.gd.core.rule.RuleResult;
import com.dave.gd.core.rule.exception.RuleRuntimeException;
import com.dave.gd.core.rule.execute.IRuleMgrExecute;
import com.dave.gd.core.rule.intf.IServiceOffer;
import com.dave.gd.core.util.AttrStatus;
import com.dave.gd.core.util.EntityHelper;
import com.dave.gd.core.util.MetaProvider;
import com.dave.gd.core.util.RetInfo;

@SuppressWarnings("rawtypes")
public abstract class AbstractBaseEntity<ID extends Serializable> extends BaseEntityMeta
    implements IBaseEntity, IBaseEntityMeta, Serializable, IRuleableEntity, IMetaEntity {
    
    protected transient ILogger logger = LoggerFactory.getLogger(this.getClass());
    
    transient ISysClass modelClass;
    
    private static IRuleMgrExecute ruleMgrExecute = null;
    
    static {
        try {
            ruleMgrExecute = ApplicationContextUtil.getBean("ruleMgrExecute");
        } catch (Exception e) {
            ruleMgrExecute = new IRuleMgrExecute() {
                @Override
                public boolean execute(AbstractBaseEntity entt, String methodName) {
                    return false;
                }
                
                @Override
                public boolean execute(AbstractBaseEntity entt, String methodName, Object... params) {
                    return false;
                }
                
            };
        }
    }
    
    private OpType enttOpType;
    
    private Set<String> loadedProperty = new HashSet<String>();
    /**
     * 动作列表
     */
    private List<IServiceOffer> orderServiceOffers;
    
    public boolean isLoaded(String propertyName) {
        return loadedProperty.contains(propertyName);
    }
    
    public void Loaded(String propertyName) {
        loadedProperty.add(propertyName);
    }
    
    /**
     * MyBatis用POJO基类
     *
     *
     */
    private static final long serialVersionUID = 1L;
    
    public AbstractBaseEntity() {
        
    }
    
    public AbstractBaseEntity(boolean genId) {
        if (genId) {
            setId(genEnttId());
        }
    }
    
    public abstract ID getId();
    
    public abstract void setId(ID id);
    
    public String getGlobalSeqName() {
        String seq = "";
        if (this.getModelClass() != null
            && !StringUtils.isNullOrEmpty(this.getModelClass().getSeqName())) {
            seq = this.getModelClass().getSeqName();
            return seq.toUpperCase();
        } else {
            Table t = this.getClass().getAnnotation(Table.class);
            if (t != null && !StringUtils.isNullOrEmpty(t.name())) {
                seq = "SEQ_" + t.name() + "_ID";
                return seq.toUpperCase();
            }
        }
        return "SEQ_GLOBAL_ID";
    }
    
    @SuppressWarnings("unchecked")
    public ID genEnttId() {
        return (ID) SequenceFetchUtils.nextId(getGlobalSeqName());
    }
    
    public static IBaseRepository defaultRepository() {
        IBaseRepository defRepository = RepositoryRegister.getInstance().getDefaultRepository();
        return defRepository;
    }
    
    @SuppressWarnings("unchecked")
    public int save() {
        return defaultRepository().insert(this);
    }
    
    @SuppressWarnings("unchecked")
    public int update() {
        return defaultRepository().update(this);
    }
    
    @SuppressWarnings("unchecked")
    public int remove() {
        return defaultRepository().delete(this);
    }
    
    public boolean executeRule(String methodName) {
        if (ruleMgrExecute == null) {
            return false;
        }
        return ruleMgrExecute.execute(this, methodName);
    }
    
    public boolean executeRule(String methodName, Object... paras) {
        if (ruleMgrExecute == null) {
            return false;
        }
        return ruleMgrExecute.execute(this, methodName, paras);
    }
    
    private ConcurrentMap<String, RuleResult> ruleResults = new ConcurrentHashMap<String, RuleResult>();
    
    @Override
    public RuleResult getRuleResultByCode(String ruleCode) {
        return ruleResults.get(ruleCode);
    }
    
    @Override
    public void addRuleResult(RuleResult result) {
        result.setSource(this);
        this.ruleResults.put(result.getRuleCode(), result);
        if (ERuleRes.ERROR == result.getRuleResult() || ERuleRes.COMFIRM == result.getRuleResult()) {
            throw new RuleRuntimeException(result.getErrorMsg(), result.getSource(), result);
        }
    }
    
    @Override
    public void removeRuleResult(RuleResult result) {
        this.ruleResults.remove(result.getRuleCode());
    }
    
    @Override
    public void replaceRuleResult(RuleResult result) {
        result.setSource(this);
        this.ruleResults.put(result.getRuleCode(), result);
    }
    
    @Override
    public List<RuleResult> getRuleResults() {
        return new ArrayList<RuleResult>(ruleResults.values());
    }
    
    @Override
    public void clearRuleResult() {
        ruleResults.clear();
    }
    
    @Override
    public boolean hasRuleResult() {
        return ruleResults != null && ruleResults.size() > 0;
    }
    
    @Override
    public List<RuleResult> getWarnResults() {
        List<RuleResult> warnResults = new ArrayList<RuleResult>();
        
        List<RuleResult> allResults = getRuleResults();
        
        for (RuleResult result : allResults) {
            if (ERuleRes.WARNING == result.getRuleResult()) {
                warnResults.add(result);
            }
        }
        return warnResults;
    }
    
    @Override
    public ISysClass getModelClass() {
        if (modelClass == null) {
            String entityname = StringUtils.substringAfterLast(this.getClass().getName(), ".");
            modelClass = MetaProvider.getEntityByName(entityname);
        }
        return modelClass;
    }
    
    @Override
    public IAttrSpec getAttrSpecByAttrName(String propertyName) {
        return this.getModelClass().getAttrSpecByCode(propertyName);
    }
    
    @Override
    public List<IAttrValue> getAttrValuesByAttrName(String propertyName) {
        return this.getModelClass().getAttrValues(propertyName);
    }
    
    @Override
    public List<IAttrSpec> getAllAttrSpec() {
        return this.getModelClass().getAllAttrSpecs();
    }
    
    @Override
    public Object get(String propertyName) {
        IAttrSpec attrSpec = this.getModelClass().getAttrSpecByCode(propertyName);
        return get(attrSpec);
    }
    
    @Override
    public Object get(Long propertyId) {
        IAttrSpec attrSpec = this.getModelClass().getAttrSpecById(propertyId);
        
        return get(attrSpec);
    }
    
    public Object get(IAttrSpec attrSpec) {
        if (attrSpec == null) {
            return null;
        }
        
        String propertyName = attrSpec.getJavaCode();
        if (StringUtils.isNullOrEmpty(propertyName)) {
            return null;
        }
        Object value = getProperty(propertyName);
        
        return value;
    }
    
    protected Object getProperty(String propertyName) {
        try {
            return PropertyUtils.getProperty(this, propertyName);
        } catch (Exception e) {
            // log
            e.printStackTrace();
            return null;
        }
    }
    
    @Override
    public RetInfo set(Long propertyId, Object value) {
        IAttrSpec attrSpec = this.getModelClass().getAttrSpecById(propertyId);
        return set(attrSpec, value, false);
    }
    
    @Override
    public RetInfo set(String propertyName, Object value) {
        IAttrSpec attrSpec = this.getModelClass().getAttrSpecByCode(propertyName);
        return set(attrSpec, value, false);
    }
    
    public RetInfo set(IAttrSpec attrSpec, Object value) {
        return set(attrSpec, value, false);
    }
    
    @Override
    public RetInfo set(String propertyName, Object value, boolean check) {
        IAttrSpec attrSpec = this.getModelClass().getAttrSpecByCode(propertyName);
        return set(attrSpec, value, check);
    }
    
    @Override
    public RetInfo set(Long propertyId, Object value, boolean check) {
        IAttrSpec attrSpec = this.getModelClass().getAttrSpecById(propertyId);
        return set(attrSpec, value, check);
    }
    
    /**
     * 
     * set
     * 	校验异常 返回RetInfo对象，设置异常，直接抛出
     * .	
     * @param attrSpec
     * @param value
     * @param check
     * @return
     * @date 2014年12月1日
     *
     */
    public RetInfo set(IAttrSpec attrSpec, Object value, boolean check) {
        RetInfo retInfo = RetInfo.newInstance();
        retInfo.setResult(BaseUnitConstants.RET_TRUE);
        
        if (attrSpec == null) {
            throw new RuntimeException("传入的属性ID或规格 ，无对应的元数据配置");
        }
        
        // 插入设置前置规则校验代码
        StringBuffer chkMsg = new StringBuffer();
        if (check == true && !this.beforeSet(attrSpec, value, chkMsg)) {
            retInfo.setRetCodeMsg(BaseUnitConstants.RET_FALSE, chkMsg.toString());
            return retInfo;
        }
        
        if (!StringUtils.isNullOrEmpty(value) && value instanceof String) {
            value = attrSpec.convertToUI(value + "", this);
        } else if (StringUtils.isNullOrEmpty(value)) {
            value = null;
        }
        
        String propertyName = attrSpec.getJavaCode();
        Object oldValue = get(attrSpec);
        
        if (ObjectUtils.nullSafeEquals(oldValue, value)) {
            retInfo.setRetCodeMsg(BaseUnitConstants.RET_FALSE, "新旧值相同！");
            return retInfo;
        }
        
        try {
            PropertyUtils.setProperty(this, propertyName, value);
        } catch (RuleRuntimeException e) {
            throw e;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            if (e.getTargetException() instanceof RuleRuntimeException) {
                throw (RuleRuntimeException) e.getTargetException();
            }
            e.printStackTrace();
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        
        // 属性修改后， 处理关联属性约束规则
        if (check == true) {
            boolean result = afterSet(attrSpec, value, chkMsg);
            if (!result) {
                retInfo.setRetCodeMsg(BaseUnitConstants.RET_FALSE, chkMsg.toString());
                return retInfo;
            }
        }
        
        return retInfo;
        
    }
    
    public OpType getEnttOpType() {
        return enttOpType;
    }
    
    public void setEnttOpType(OpType type) {
        // 重置动作:删除 + 新增（值不变）
        // edit by hehuang
        // 2014/11/29
        if (type == null) {
            this.enttOpType = null;
        } else if (enttOpType == null) {
            this.enttOpType = type;
        } else {
            if (OpType.ADD.equals(enttOpType)) {
                if (OpType.MOD.equals(type)) {
                    this.enttOpType = OpType.ADD;
                } else if (OpType.DEL.equals(type)) {
                    this.enttOpType = OpType.DEL;
                }
            } else if (OpType.MOD.equals(enttOpType)) {
                if (OpType.ADD.equals(type)) {
                    this.enttOpType = OpType.MOD;
                } else if (OpType.DEL.equals(type)) {
                    this.enttOpType = OpType.DEL;
                }
            } else if (OpType.DEL.equals(enttOpType)) {
                if (OpType.ADD.equals(type)) {
                    this.enttOpType = OpType.ADD;
                } else if (OpType.MOD.equals(type)) {
                    this.enttOpType = OpType.MOD;
                }
            }
        }
    }
    
    /**
     * 
     * beforeSet
     * 	属性设置前置规则校验，如：长度、取值范围、非空等约束 .
     * 	使用说明：只有在单个属性设置的时候调用；不对实例的动作进行过滤
     * .	
     * @param attr
     * 			属性规格
     * @param value
     * 			属性值
     * @param chkMsg
     * 			校验信息
     * @return
     * 			校验结果
     * @date 2014/11/25
     *
     */
    protected boolean beforeSet(IAttrSpec attr, Object value, StringBuffer chkMsg) {
        if (SessionContext.getRuleMode() == ERuleMode.DISABLE) {
            return true;
        }
        
        // 规则校验
        // 属性设置前触发的规则
        this.executeRule("beforeSet", attr, value);
        
        // 通过
        return attr.beforeSet(this, value, chkMsg);
    }
    
    /**
     * 
     * afterSet
     * 	属性设置后置规则校验，如：属性间依赖、互斥等 .
     *  使用说明：只有在单个属性设置的时候调用；不对实例的动作进行过滤
     * .	
     * @param attr
     * 			属性规格
     * @param value
     * 			属性值
     * @param chkMsg
     * 			校验信息
     * @return
     * 			校验结果
     * @date 2014/11/25
     *
     */
    protected boolean afterSet(IAttrSpec attr, Object value, StringBuffer chkMsg) {
        if (SessionContext.getRuleMode() == ERuleMode.DISABLE) {
            return true;
        }
        
        // 属性设置后触发的规则
        this.executeRule("afterSet", attr, value);
        
        return attr.afterSet(this, value, chkMsg);
    }
    
    /**
     * 实体对象校验
     * .
     * 
     */
    public boolean validate() {
        Boolean result = this.executeRule("validate");
        return result;
    }
    
    /**
     * 
     * 实体数据补充.
     * 
     * @return
     * @author Luxb
     *          2014年11月26日 Luxb
     */
    public boolean supplyData() {
        Boolean result = this.executeRule("supplyData");
        return result;
    }
    
    /**
     * 业务校验.
     * @return
     * @author Luxb
     *          2014年11月26日 Luxb
     */
    public boolean businessValidate() {
        Boolean result = this.executeRule("businessValidate");
        return result;
    }
    
    /**
     * 派生.
     * @return
     * @author Luxb
     *          2014年11月26日 Luxb
     */
    public boolean derive() {
        Boolean result = this.executeRule("derive");
        return result;
    }
    
    /**
     * 
     * .
     * 
     * @param isDri
     * @return
     * @author Luxb
     * 2014年11月28日 Luxb
     */
    public boolean validateContainer(boolean isDri) {
        validate();
        
        supplyData();
        
        businessValidate();
        
        if (isDri) {
            derive();
        }
        return true;
    }
    
    public List<IServiceOffer> getOrderServiceOffers() {
        return orderServiceOffers;
    }
    
    public void setOrderServiceOffers(List<IServiceOffer> orderServiceOffers) {
        this.orderServiceOffers = orderServiceOffers;
    }
    
    public void setOrderServiceOffer(IServiceOffer so) {
        setOrderServiceOffer(so, true);
    }
    
    public void setOrderServiceOffer(IServiceOffer so, boolean isRunRule) {
        if (so != null) {
            boolean hasCreate = false;
            if (this.getOrderServiceOffers() == null) {
                this.orderServiceOffers = new ArrayList<IServiceOffer>();
            } else if (!OpType.ADD.equals(so.opType())) { //当动作不为新装时候，才需要判断原有动作是否包含新装
                //如果没有包含新增动作，才清空列表，否则要保留新增动作
                for (IServiceOffer serviceOffer : this.orderServiceOffers) {
                    if (OpType.ADD.equals(serviceOffer.opType())) {
                        hasCreate = true;
                    }
                }
                if (!hasCreate) {
                    this.orderServiceOffers.clear();
                }
            } else {
                this.orderServiceOffers.clear();
            }
            if (!this.orderServiceOffers.contains(so)) {
                if (!hasCreate) { //当有新装动作时无需添加其他动作
                    this.orderServiceOffers.add(so);
                }
                //this.executeRule("setOrderServiceOffer", so);
                /*if(isRunRule){
                    ContextHelper.executeRule(call, this, "setOrderServiceOffer", so);
                }*/
            }
        }
    }
    
    /**
     * 
     * 方法功能:用于DTO转entity时，对象的拷贝，list转换为Map
     *  .
     * @param retRuleResults
     * @author: linzq
     * @修改记录： 
     * ==============================================================<br>
     * 日期:2014-11-28 linzq 创建方法，并实现其功能
     * ==============================================================<br>
     */
    public void setRetRuleResults(List<RuleResult> retRuleResults) {
        for (RuleResult result : retRuleResults) {
            ruleResults.put(result.getRuleCode(), result);
        }
        SessionContext.collectAllResults(retRuleResults);
    }
    
    /**
     * 根据属性规格高， 获取属性初始值.
     * 
     * @param p
     *            属性规格
     * @return 属性原始值
     */
    public Object getInitValue(IAttrSpec p) {
        return EntityHelper.getInitValue(this, p);
    }
    
    /**
     * 方法功能: 根据属性名称，读取属性值.
     * 
     * @param propertyName
     *            属性名称
     * @return Object value
     */
    public Object getInitValue(String propertyName) {
        IAttrSpec p = this.getModelClass().getAttrSpecByCode(propertyName);
        
        return getInitValue(p);
    }
    
    /**
     * 
     * 方法功能:
     *  获取属性状态列表.
     * @return
     * @author: linzq
     * @修改记录： 
     * ==============================================================<br>
     * 日期:2014-12-3 linzq 创建方法，并实现其功能
     * ==============================================================<br>
     */
    public List<AttrStatus> getAttrStatusList() {
        return EntityHelper.getEnttCompare(this);
    }
}
